package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.util.PathUtil;
import cc.shacocloud.mirage.restful.util.RoutingContextUtils;
import cc.shacocloud.mirage.utils.PathMatcher;
import cc.shacocloud.mirage.utils.collection.SelfSortSet;
import cc.shacocloud.mirage.utils.comparator.Ordered;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.ext.web.Router;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 基于 vertx {@link Router} 的过滤器映射处理程序
 */
@Slf4j
public class VertxRouterFilterMappingHandler extends AbstractApplicationContextAware implements RouterMappingHandler {
    
    private static final String FILTER_INDEX_KEY = ".mirage_FILTER_INDEX";
    
    private final List<FilterMappingInfo> filters = new SelfSortSet<>(FilterMappingInfo::getOrder, FilterMappingInfo::getFilterName);
    
    /**
     * 路径匹配器
     */
    @Setter
    private PathMatcher pathMatcher = PathUtil.DEFAULT_PATH_MATCHER;
    
    /**
     * 添加过滤器
     *
     * @param filters 一个或多个过滤器
     * @return {@link VertxRouterFilterMappingHandler}
     */
    public VertxRouterFilterMappingHandler addFilter(FilterMappingInfo... filters) {
        this.filters.addAll(Arrays.asList(filters));
        return this;
    }
    
    /**
     * 添加过滤器
     *
     * @param filterArr 过滤器集合
     * @return {@link VertxRouterFilterMappingHandler}
     */
    public VertxRouterFilterMappingHandler addFilter(List<FilterMappingInfo> filterArr) {
        this.filters.addAll(filterArr);
        return this;
    }
    
    /**
     * 处理过滤器
     */
    public void handleFilter(io.vertx.ext.web.RoutingContext ctx) {
        RoutingContext context = RoutingContextUtils.createVertXRoutingContext(ctx);
        
        // 如果未绑定过滤器则执行跳过
        if (this.filters.size() == 0) {
            context.next();
            return;
        }
        
        // 当前执行到的过滤器位置索引
        AtomicInteger index = context.get(FILTER_INDEX_KEY);
        if (index == null) {
            index = new AtomicInteger(-1);
            context.put(FILTER_INDEX_KEY, index);
        }
        
        doInternalFilter(context);
    }
    
    /**
     * 内部过滤器执行逻辑
     */
    private void doInternalFilter(@NotNull RoutingContext context) {
        final AtomicInteger index = context.get(FILTER_INDEX_KEY);
        if (Objects.isNull(index)) {
            throw new IllegalStateException("过滤器索引对象不存在，请检查是否被人为删除！");
        }
        
        if (index.incrementAndGet() >= filters.size()) {
            context.next();
            return;
        }
        
        FilterMappingInfo mappingInfo = filters.get(index.get());
        
        // 匹配过滤器
        String[] excludePatterns = mappingInfo.getExcludePatterns();
        String[] includePatterns = mappingInfo.getIncludePatterns();
        boolean matches = PathUtil.matches(new String[]{context.normalizedPath()}, pathMatcher, excludePatterns, includePatterns);
        
        if (!matches) {
            doInternalFilter(context);
        }
        
        Filter filter = mappingInfo.getFilter();
        
        // 执行销毁
        context.addEndHandler(event -> {
            Promise<Void> promise = Promise.promise();
            filter.destroy(promise);
            promise.future().onFailure(cause -> {
                // 过滤器销毁错误
                if (log.isWarnEnabled()) log.warn("", cause);
            });
        });
        
        // 执行
        Promise<RoutingContext> contextPromise = Promise.promise();
        filter.doFilter(context, contextPromise);
        contextPromise.future().onComplete(ar -> {
            // 执行成功并且 结果上下文不为空则继续调用
            if (ar.succeeded()) {
                RoutingContext resultCtx = ar.result();
                if (Objects.nonNull(resultCtx)) {
                    doInternalFilter(resultCtx);
                }
            } else {
                // 过滤器处理错误
                if (log.isWarnEnabled()) log.warn("", ar.cause());
                HttpResponse response = context.response();
                response.setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code());
                response.end();
            }
        });
    }
    
    @Override
    public Future<Void> bindRouterMapping(@NotNull Router router) {
        router.route().handler(this::handleFilter);
        return Future.succeededFuture();
    }
    
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}
